/** * Copyright (c) 2011 Mateusz Parzonka * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * */ package com.github.parzonka.ccms.sorter.comparator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.util.CompilationUnitSorter; import org.eclipse.jdt.internal.corext.codemanipulation.SortMembersOperation.DefaultJavaElementComparator; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Comparator that sorts all {@link BodyDeclaration}s that are methods by an * ordering associated with their unique method signature. This ordering is * pre-calculated and passed at instantiation. All other BodyDeclarations are * tried to be sorted preserving their old relative order. * <p> * Based on {@link DefaultJavaElementComparator}. * * @author Mateusz Parzonka */ public class BodyDeclarationComparator implements Comparator<BodyDeclaration> { final private static Logger logger = LoggerFactory.getLogger(BodyDeclarationComparator.class); private final MembersOrderPreferenceCache fMemberOrderCache; private final Comparator<Signature> methodDeclarationComparator; private final Set<Signature> knownMethodSignatures; public BodyDeclarationComparator(Comparator<Signature> methodDeclarationComparator, Set<Signature> knownMethodSignatures) { this.fMemberOrderCache = JavaPlugin.getDefault().getMemberOrderPreferenceCache(); this.methodDeclarationComparator = methodDeclarationComparator; this.knownMethodSignatures = knownMethodSignatures; } /** * @return the knownMethodSignatures */ public Set<Signature> getKnownMethodSignatures() { return this.knownMethodSignatures; } public Comparator<Signature> getMethodDeclarationComparator() { return this.methodDeclarationComparator; } /** * This comparator follows the contract defined in * CompilationUnitSorter.sort. * * @see Comparator#compare(java.lang.Object, java.lang.Object) * @see CompilationUnitSorter#sort(int, * org.eclipse.jdt.core.ICompilationUnit, int[], java.util.Comparator, * int, org.eclipse.core.runtime.IProgressMonitor) */ @Override public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) { if (isSortPreserved(bodyDeclaration1) && isSortPreserved(bodyDeclaration2)) { final int preservedOrder = preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); logger.trace("Keeping preserved order: [{}] : [{}] = " + preservedOrder, bodyDeclaration1.toString(), bodyDeclaration2.toString()); return preservedOrder; } final int cat1 = category(bodyDeclaration1); final int cat2 = category(bodyDeclaration2); if (cat1 != cat2) { final int categoryOrder = cat1 - cat2; logger.trace("Keeping category order: [{}] : [{}] = " + categoryOrder, bodyDeclaration1.toString(), bodyDeclaration2.toString()); return categoryOrder; } if (bodyDeclaration1.getNodeType() == ASTNode.METHOD_DECLARATION) { final MethodDeclaration method1 = (MethodDeclaration) bodyDeclaration1; final MethodDeclaration method2 = (MethodDeclaration) bodyDeclaration2; final Signature signature1 = new Signature(method1); final Signature signature2 = new Signature(method2); if (this.knownMethodSignatures.contains(signature1) && this.knownMethodSignatures.contains(signature2)) { final int compare = this.methodDeclarationComparator.compare(signature1, signature2); logger.trace("Comparing methods [{}] : [{}] = " + compare, signature1, signature2); if (compare == 0) logger.warn("No absolute compare value between [{}] and [{}]", signature1, signature2); return compare; } logger.warn("A method signature was not known!"); logger.warn("{} known={}", signature1, this.knownMethodSignatures.contains(signature1)); logger.warn("{} known={}", signature2, this.knownMethodSignatures.contains(signature2)); } final int relativeOrder = preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); logger.trace("Keeping relative order: [{}] : [{}]= " + relativeOrder, bodyDeclaration1.toString(), bodyDeclaration2.toString()); return relativeOrder; } private boolean isSortPreserved(BodyDeclaration bodyDeclaration) { switch (bodyDeclaration.getNodeType()) { case ASTNode.FIELD_DECLARATION: case ASTNode.ENUM_CONSTANT_DECLARATION: case ASTNode.INITIALIZER: case ASTNode.TYPE_DECLARATION: return true; default: return false; } } private int preserveRelativeOrder(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) { final int value1 = ((Integer) bodyDeclaration1.getProperty(CompilationUnitSorter.RELATIVE_ORDER)).intValue(); final int value2 = ((Integer) bodyDeclaration2.getProperty(CompilationUnitSorter.RELATIVE_ORDER)).intValue(); return value1 - value2; } private int category(BodyDeclaration bodyDeclaration) { switch (bodyDeclaration.getNodeType()) { case ASTNode.METHOD_DECLARATION: { final MethodDeclaration method = (MethodDeclaration) bodyDeclaration; if (method.isConstructor()) { return getMemberCategory(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX); } else return getMemberCategory(MembersOrderPreferenceCache.METHOD_INDEX); } case ASTNode.FIELD_DECLARATION: { return getMemberCategory(MembersOrderPreferenceCache.FIELDS_INDEX); } case ASTNode.INITIALIZER: { final int flags = ((Initializer) bodyDeclaration).getModifiers(); if (Modifier.isStatic(flags)) return getMemberCategory(MembersOrderPreferenceCache.STATIC_INIT_INDEX); else return getMemberCategory(MembersOrderPreferenceCache.INIT_INDEX); } case ASTNode.TYPE_DECLARATION: case ASTNode.ENUM_DECLARATION: case ASTNode.ANNOTATION_TYPE_DECLARATION: return getMemberCategory(MembersOrderPreferenceCache.TYPE_INDEX); case ASTNode.ENUM_CONSTANT_DECLARATION: return getMemberCategory(MembersOrderPreferenceCache.ENUM_CONSTANTS_INDEX); case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: return getMemberCategory(MembersOrderPreferenceCache.METHOD_INDEX); } throw new IllegalStateException(); } private int getMemberCategory(int kind) { return this.fMemberOrderCache.getCategoryIndex(kind); } @Override public String toString() { final String LF = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(); sb.append("Final ordering of [" + this.knownMethodSignatures.size() + "] known signatures:").append(LF); final List<Signature> orderedSignatures = new ArrayList<Signature>(); orderedSignatures.addAll(this.knownMethodSignatures); Collections.sort(orderedSignatures, this.methodDeclarationComparator); int i = 0; for (final Signature signature : orderedSignatures) { sb.append(String.format("[%d] %s", ++i, signature)).append(LF); } return sb.toString(); } }